import React, { createRef, PureComponent } from 'react';
import cx from 'classnames';
import PropTypes from 'prop-types';
import createVideo from 'video.js';

import 'video.js/dist/video-js.css';
import S from './index.m.less';

const ETs = {
    ready: 'ready',
    loaded: 'loadedmetadata',
    play: 'play',
    pause: 'pause',
    end: 'end',
    canplay: 'canplay',
    waiting: 'waiting',
    playing: 'playing',
    timeupdate: 'timeupdate',
    error: 'error',
};

/**
 * 视频直播组件
 */
export default class H5Video extends PureComponent {
    static defaultProps = {
        className: '',
        poster: undefined,
        type: 'application/x-mpegURL',
        onLoaded: undefined,
        onPlay: undefined,
        onPause: undefined,
        onEnd: undefined,
        onError: undefined,
        onTimeupdate: undefined,
        onCanplay: undefined,
        onWaiting: undefined,
        onPlaying: undefined,
        onDispose: undefined,
        isHorizontal: false,
        video: {},
        h5: {},
    };

    static propTypes = {
        src: PropTypes.string.isRequired, // 视频地址
        className: PropTypes.string, // css class
        poster: PropTypes.string, // 封面
        type: PropTypes.string, // 视频类型
        onReady: PropTypes.func.isRequired, // videojs 初始化完成
        onLoaded: PropTypes.func, // 视频正常加载
        onPlay: PropTypes.func, // 播放回调
        onPause: PropTypes.func, // 暂停回调
        onEnd: PropTypes.func, // 播放结束回调
        onError: PropTypes.func, // 错误回调
        onTimeupdate: PropTypes.func,
        onCanplay: PropTypes.func, // 可以播放回调
        onWaiting: PropTypes.func, // 等待加载
        onPlaying: PropTypes.func, // 播放中
        onDispose: PropTypes.func, // 销毁
        isHorizontal: PropTypes.bool, // 是否横屏直播
        video: PropTypes.object, // videojs实例化参数
        h5: PropTypes.object, // h5 video标签属性
    };

    state = {};

    videoRef = createRef();

    constructor(props) {
        super(props);
        // 保证 poster 加载
        this.posterPromise = new Promise((resolve) => {
            // 图片加载超时，最长3秒
            const timeout = setTimeout(resolve, 3000);
            if (props.poster) {
                const img = new Image(props.poster);
                img.onload = () => {
                    clearTimeout(timeout);
                    setTimeout(resolve, 1200);
                };
                img.src = props.poster;
            }
        });
    }

    componentDidMount() {
        this.initVideo();
    }

    UNSAFE_componentWillReceiveProps(nextProps) {
        if (!nextProps.src) {
            this.pause();
        }
        if (nextProps.isHorizontal) {
            this.player && this.player.addClass('horizontal');
        } else {
            this.player && this.player.removeClass('horizontal');
        }
    }

    componentWillUnmount() {
        WPT.Modal.loading.close();
        this.disposeVideo();
        Object.values(ETs).forEach((e) => this.player && this.player.off(e));
    }

    ready = () => {};

    loaded = (...args) => {
        setTimeout(() => {
            WPT.Modal.loading.close();

            this?.posterPromise.then(() => {
                if (this?.player) {
                    const { onLoaded } = this.props;
                    onLoaded && onLoaded(...args);
                    this.player.addClass('loaded');
                }
            });
        }, 500);
    };

    play = () => {
        const { onError } = this.props;
        try {
            this.player && this.player.play();
        } catch (e) {
            onError && onError(e);
        }
    };

    pause = () => {
        try {
            this.player && this.player.pause();
        } catch (e) {
            // do nothing
        }
    };

    end = () => {};

    canplay = () => {};

    waiting = () => {};

    playing = () => {};

    timeupdate = () => {};

    error = () => {};

    initVideo = () => {
        const {
            onReady,
            onPlay,
            onPause,
            onEnd,
            onError,
            onTimeupdate,
            onCanplay,
            onWaiting,
            onPlaying,
            video,
        } = this.props;

        const { videoRef, bindEvent } = this;

        try {
            this.player = createVideo(
                videoRef && videoRef.current,
                {
                    BigPlayButton: false,
                    autoplay: false,
                    controls: false,
                    errorDisplay: false,
                    ...video,
                },
                () => {
                    onReady && onReady(this.player);
                    bindEvent(ETs.loaded, this.loaded);
                    bindEvent(ETs.play, onPlay);
                    bindEvent(ETs.pause, onPause);
                    bindEvent(ETs.end, onEnd);
                    bindEvent(ETs.timeupdate, onTimeupdate);
                    bindEvent(ETs.canplay, onCanplay);
                    bindEvent(ETs.waiting, onWaiting);
                    bindEvent(ETs.playing, onPlaying);
                }
            );
            bindEvent(ETs.error, onError);
        } catch (e) {
            onError && onError(e);
        }
    };

    disposeVideo = () => {
        const { onDispose } = this.props;
        onDispose && onDispose();
        if (this.player) {
            this.player.pause();
            this.player.dispose();
        }
        this.player = null;
    };

    bindEvent = (eventType, callback) => {
        if (this.player && eventType && callback) {
            this.player.on(eventType, (...args) => {
                this.player && callback(...args);
            });
        }
    };

    render() {
        const { className, poster, src, type, h5 } = this.props;

        return (
            <video
                className={cx(S.h5Video, className)}
                poster={poster}
                webkit-playsinline="true"
                playsInline
                x5-video-player-type="h5-page"
                ref={this.videoRef}
                {...h5}
            >
                <source src={src} type={type} />
            </video>
        );
    }
}
